﻿#pragma once

#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <memory>

#include "ImageShow/CvImageShow.h"

#ifdef _USE_QT_
#include <QtGui/QPainter>
#include <QtCore/QRect>
#include <QtCore/QPoint>
#endif

//#define _SUPPORT_PROTO_

#ifdef _SUPPORT_PROTO_
	#include "Utils/ProtoIO.hpp"
	#include "ShapeMessage.pb.h"
#endif

enum ShapeGroupEnum
{
	ShapeGroupPoint = 0,
	ShapeGroupLine = 1,
	ShapeGroupRect = 2,
    ShapeGroupCircle = 3,
    ShapeGroupPolygon = 4,
	ShapeGroups,

	ShapeGroupRuler = 10,
};

namespace CvImageShow{
	namespace Shapes{

		typedef struct tagShapeDef 
		{
			std::string type;
			std::string name;
			int groupid;
			int subindex;
			union {
				struct {
					unsigned char red;
					unsigned char green;
					unsigned char blue;
					unsigned char resver;
				};
				int color;
			};
			std::vector<cv::Point2f> ptPoints;
			bool enable;
			bool visible;
			int dragmode;

			void Init(std::string strType, std::string strName, int nGroupId, int nSubIndex, std::vector<cv::Point2f> vPtPoints)
			{
				type = strType;
				name = strName;
				groupid = nGroupId;
				subindex = nSubIndex;
				setColor(120, 20, 20);
				enable = true;
				visible = true;
				dragmode = 0;
				ptPoints.clear();
				for (auto item : vPtPoints)
				{
					ptPoints.push_back(item);
				}
			}

			void Init(std::string strType, std::string strName, int nGroupId, int nSubIndex, 
				std::vector<cv::Point2f> vPtPoints, int nColor, bool bEnable, bool bVisible, int nDragmode)
			{
				Init(strType, strName, nGroupId, nSubIndex, vPtPoints);
				color = nColor;
				enable = bEnable;
				visible = bVisible;
				dragmode = nDragmode;
			}

			void setColor(unsigned char ucRed, unsigned char ucGreen, unsigned char ucBlue)
			{
				red = ucRed;
				green = ucGreen;
				blue = ucBlue;
			}
			void setColor(int nColor) { color = nColor; }

			void SetEnableValue(bool bEnable) { enable = bEnable; }
			void SetVisibleValue(bool bVisible) { visible = bVisible; }
			void SetDragmode(int nDragmode) { dragmode = nDragmode; }
            void SetName(std::string _name) { name = _name; };
		}ShapeDef;

		class CV_EXPORT CShape
		{
		protected:
			ShapeDef m_hDefine;
			std::vector<cv::Point2f> m_vPtCorners;
		public:
			explicit CShape(const ShapeDef& param);

			~CShape();

			bool IsMe(int nGroupID, int nSubIndex){
				return m_hDefine.groupid == nGroupID && m_hDefine.subindex == nSubIndex;
			}

			bool IsGroupMember(int nGroupID){
				return m_hDefine.groupid == nGroupID;
			}

            virtual int IsSelected(const cv::Point& ptPos, float fScale){
                ptPos;
                fScale;
				return m_nSelectedStatus;
			}
			virtual bool SetPointsIsOk(const std::vector<cv::Point2f>& vPoints) = 0;
#ifdef _USE_QT_
			virtual void DrawSelf(QPainter* pPainter, const cv::Point2f& ptOffset, const float fScaleRatio) = 0;
			void DrawTitle(QPainter* pPainter, const cv::Point2f& ptOffset, float fScaleRatio);
#else
			virtual void DrawSelf(Gdiplus::Graphics* pGdiPainter, const cv::Point2f& ptOffset, const float fScaleRatio) = 0;
			void DrawTitle(Gdiplus::Graphics* pGdiPainter, const cv::Point2f& ptOffset, float fScaleRatio);
#endif

			virtual void AdjustDragDelta(const cv::Point2f& ptDelta) = 0;

			std::string GetTypeName(){ return m_hDefine.type; }
			std::string GetTitle() { return m_hDefine.name; }
			int GetColorRed() { return m_hDefine.red; }
			int GetColorGreen() { return m_hDefine.green; }
			int GetColorBlue() { return m_hDefine.blue; }
			int GetColor() { return m_hDefine.color; }
			int GetGroupID() { return m_hDefine.groupid; }
			int GetSubIndex() { return m_hDefine.subindex; }

			virtual std::vector<cv::Point2f>& GetPoints() = 0;

			int GetCornersCount(){
				return static_cast<int>(m_vPtCorners.size());
			}

			int GetSelectedId(){ return m_nSelectedID; }

			void SetColor(int nRed, int nGreen, int nBlue) { m_hDefine.setColor(nRed, nGreen, nBlue); }

			void SetSelectFlag(int nFlag){ m_nSelectFlag = nFlag; }

			void DisselectStatus(){ m_nSelectedStatus = 0; }
#ifdef _USE_QT_
			void DrawCorners(QPainter* Painter, const cv::Point2f& ptOffset, const float fScaleRatio);
#else
			void DrawCorners(Gdiplus::Graphics* pGdiPainter, const cv::Point2f& ptOffset, const float fScaleRatio);
#endif

            //设置多线是否闭合
            virtual bool SetIsClose(bool IsClose)
            {
                m_IsClose = IsClose;
                return m_IsClose;
            }

            virtual bool GetIsClose()
            {
                return m_IsClose;
            }

		protected:
			enum ENUM_SELECT_STATUS
			{
				_SELECT_NONE_ = 0,
				_SELECT_WHOLE_,
				_SELECT_CORNER_,
				_SELECT_EDGE_,
			};

			uint32_t m_nSelectedStatus;
			uint32_t m_nSelectedID;
			uint32_t m_nSelectFlag;
            bool m_IsClose; //是否闭合

			cv::Rect2f CreateOneCorner(cv::Point2f ptCorner, int nRadio);
			int WhichCornerSelected(const cv::Point2f& ptPos, float fScaleRatio);
			int WhichEdgeSelected(const cv::Point2f& ptPos, float fScaleRatio);
			bool IsSelectedLine(const cv::Point2f& ptPos, const cv::Point2f& ptLine_0, const cv::Point2f& ptLine_1, float fScaleRatio);
		};		
		// end for CShape class


		class CV_EXPORT CShapeArray
		{
		public:
			CShapeArray();
			~CShapeArray();

			void AddItem(std::string strType, std::string strTitle, int nGroupID, int nSubindex,
						int nRed, int nGreen, int nBlue, int nFlag);
			std::vector<cv::Point2f> m_vPoints;
			void AddItem(std::string strType, std::string strTitle, int nGroupID, int nSubindex,
				const std::vector<cv::Point2f> vPts, int nRed, int nGreen, int nBlue, int nFlag);

			void AddCircle();
			void AddLine();
			void AddPoint();
			void AddRect();

			void AddItem(const ShapeDef& shapedefine);

			int WhichItemSelect(const cv::Point2f& ptPos, const float fScale);
			void AdjustDragDelta(const cv::Point2f& ptDelta);
			CShape* GetSelectedItem(){ return m_pSelectedShape; }

			CShape* GetFirstItem();

			void DeleteItem(const int nGroupID, const int nSubIndex);
			void DeleteGroupItems(const int nGroupID);

			CShape* FindShape(const int nGroupID, const int nSubIndex);

#ifdef _SUPPORT_PROTO_
			void SaveShapesToFile(const std::string& strFileName);
			void ReadShapesFromFile(const std::string& strFileName);
#endif

#ifdef _USE_QT_
			void DrawItems(QPainter* pPainter, const cv::Point2f& ptOffset, const float fScale);
#else
			void DrawItems(Gdiplus::Graphics* pGdiPainter, const cv::Point2f& ptOffset, const float fScale);
#endif
		private:
			std::vector <std::shared_ptr<CShape> > m_vShapes;
			CShape* m_pSelectedShape;

			
			std::shared_ptr<CShape> CreateNewItem(const ShapeDef& shapedefine);

#ifdef _SUPPORT_PROTO_
			void ConvShapeStructToProto(CShape* pShape, ShapeProto::ShapeMessage* pDef);
			void LoadShapeItemsFromFile(const std::string& strFileName, google::protobuf::Message& msg);
			void WriteShapeItemsToFile(const std::string& strFileName, google::protobuf::Message& msg);
#endif
		};	// end for class CShapeArray
	}	// end for namespace Shapes
}	// end for namespace Shapes

